--- /dev/null
+use std::io::IoError;
+use std::io;
+use std::str;
+
+use core::{Package, PackageSet, Resolve, Target};
+use util;
+use util::{CargoResult, ChainError, internal, Config};
+
+pub struct Context<'a, 'b> {
+ pub deps_dir: Path,
+ pub primary: bool,
+ pub rustc_version: String,
+ pub config: &'b mut Config<'b>,
+
+ dest: Path,
+ host_dylib: (String, String),
+ package_set: &'a PackageSet,
+ resolve: &'a Resolve,
+ target_dylib: (String, String),
+}
+
+impl<'a, 'b> Context<'a, 'b> {
+ pub fn new(resolve: &'a Resolve, deps: &'a PackageSet,
+ config: &'b mut Config<'b>,
+ dest: Path, deps_dir: Path) -> CargoResult<Context<'a, 'b>> {
+ let target_dylib = try!(Context::dylib_parts(config.target()));
+ let host_dylib = if config.target().is_none() {
+ target_dylib.clone()
+ } else {
+ try!(Context::dylib_parts(None))
+ };
+ Ok(Context {
+ rustc_version: try!(Context::rustc_version()),
+ dest: dest,
+ deps_dir: deps_dir,
+ primary: false,
+ resolve: resolve,
+ package_set: deps,
+ config: config,
+ target_dylib: target_dylib,
+ host_dylib: host_dylib,
+ })
+ }
+
+ /// Run `rustc` to figure out what its current version string is
+ fn rustc_version() -> CargoResult<String> {
+ let output = try!(util::process("rustc").arg("-v").arg("verbose")
+ .exec_with_output());
+ Ok(String::from_utf8(output.output).unwrap())
+ }
+
+ /// Run `rustc` to discover the dylib prefix/suffix for the target
+ /// specified.
+ fn dylib_parts(target: Option<&str>) -> CargoResult<(String, String)> {
+ let process = util::process("rustc")
+ .arg("-")
+ .arg("--crate-name").arg("-")
+ .arg("--crate-type").arg("dylib")
+ .arg("--print-file-name");
+ let process = match target {
+ Some(s) => process.arg("--target").arg(s),
+ None => process,
+ };
+ let output = try!(process.exec_with_output());
+
+ let output = str::from_utf8(output.output.as_slice()).unwrap();
+ let parts: Vec<&str> = output.trim().split('-').collect();
+ assert!(parts.len() == 2, "rustc --print-file-name output has changed");
+
+ Ok((parts.get(0).to_string(), parts.get(1).to_string()))
+ }
+
+ /// Prepare this context, ensuring that all filesystem directories are in
+ /// place.
+ pub fn prepare(&self, pkg: &Package) -> CargoResult<()> {
+ debug!("creating target dir; path={}", self.dest.display());
+
+ try!(self.mk_target(&self.dest).chain_error(||
+ internal(format!("Couldn't create the target directory for {} at {}",
+ pkg.get_name(), self.dest.display()))));
+
+ try!(self.mk_target(&self.deps_dir).chain_error(||
+ internal(format!("Couldn't create the directory for dependencies for {} at {}",
+ pkg.get_name(), self.deps_dir.display()))));
+
+ Ok(())
+ }
+
+ fn mk_target(&self, target: &Path) -> Result<(), IoError> {
+ io::fs::mkdir_recursive(target, io::UserRWX)
+ }
+
+ /// Switch this context over to being the primary compilation unit,
+ /// affecting the output of `dest()` and such.
+ pub fn primary(&mut self) {
+ self.primary = true;
+ }
+
+ /// Return the destination directory for output.
+ pub fn dest<'a>(&'a self) -> &'a Path {
+ if self.primary {&self.dest} else {&self.deps_dir}
+ }
+
+ /// Return the (prefix, suffix) pair for dynamic libraries.
+ ///
+ /// If `plugin` is true, the pair corresponds to the host platform,
+ /// otherwise it corresponds to the target platform.
+ fn dylib<'a>(&'a self, plugin: bool) -> (&'a str, &'a str) {
+ let pair = if plugin {&self.host_dylib} else {&self.target_dylib};
+ (pair.ref0().as_slice(), pair.ref1().as_slice())
+ }
+
+ /// Return the exact filename of the target.
+ pub fn target_filename(&self, target: &Target) -> String {
+ let stem = target.file_stem();
+
+ if target.is_dylib() {
+ let (prefix, suffix) = self.dylib(target.get_profile().is_plugin());
+ format!("{}{}{}", prefix, stem, suffix)
+ } else if target.is_rlib() {
+ format!("lib{}.rlib", stem)
+ } else {
+ unreachable!()
+ }
+ }
+
+ /// For a package, return all targets which are registered as dependencies
+ /// for that package.
+ pub fn dep_targets(&self, pkg: &Package) -> Vec<Target> {
+ let deps = match self.resolve.deps(pkg.get_package_id()) {
+ None => return vec!(),
+ Some(deps) => deps,
+ };
+ deps.map(|pkg_id| {
+ self.package_set.iter()
+ .find(|pkg| pkg_id == pkg.get_package_id())
+ .expect("Should have found package")
+ })
+ .filter_map(|pkg| {
+ pkg.get_targets().iter().find(|&t| {
+ t.is_lib() && t.get_profile().is_compile()
+ })
+ })
+ .map(|t| t.clone())
+ .collect()
+ }
+}
use std::collections::HashMap;
use std::hash::Hasher;
use std::hash::sip::SipHasher;
-use std::io::{File, IoError};
-use std::io;
+use std::io::File;
use std::os::args;
-use std::str;
use term::color::YELLOW;
use core::{Package, PackageSet, Target, Resolve};
use util;
-use util::{CargoResult, ChainError, ProcessBuilder, CargoError, internal, human};
+use util::{CargoResult, ProcessBuilder, CargoError, human};
use util::{Config, TaskPool, DependencyQueue, Fresh, Dirty, Freshness};
use self::job::Job;
+use self::context::Context;
mod job;
+mod context;
type Args = Vec<String>;
-struct Context<'a, 'b> {
- dest: &'a Path,
- deps_dir: &'a Path,
- primary: bool,
- rustc_version: &'a str,
- resolve: &'a Resolve,
- package_set: &'a PackageSet,
- config: &'b mut Config<'b>,
- dylib: (String, String)
-}
-
// This is a temporary assert that ensures the consistency of the arguments
// given the current limitations of Cargo. The long term fix is to have each
// Target know the absolute path to the build location.
.join(uniq_target_dest(targets).unwrap_or(""));
let deps_target_dir = target_dir.join("deps");
- let output = try!(util::process("rustc").arg("-v").exec_with_output());
- let rustc_version = str::from_utf8(output.output.as_slice()).unwrap();
+ let mut cx = try!(Context::new(resolve, deps, config,
+ target_dir, deps_target_dir));
// First ensure that the destination directory exists
- debug!("creating target dir; path={}", target_dir.display());
-
- try!(mk_target(&target_dir).chain_error(||
- internal(format!("Couldn't create the target directory for {} at {}",
- pkg.get_name(), target_dir.display()))));
-
- try!(mk_target(&deps_target_dir).chain_error(||
- internal(format!("Couldn't create the directory for dependencies for {} at {}",
- pkg.get_name(), deps_target_dir.display()))));
-
- let output = try!(util::process("rustc")
- .arg("-")
- .arg("--crate-name").arg("-")
- .arg("--crate-type").arg("dylib")
- .arg("--print-file-name")
- .exec_with_output());
-
- let output = str::from_utf8(output.output.as_slice()).unwrap();
-
- let parts: Vec<&str> = output.slice_to(output.len() - 1).split('-').collect();
- assert!(parts.len() == 2, "rustc --print-file-name output has changed");
-
- let mut cx = Context {
- dest: &deps_target_dir,
- deps_dir: &deps_target_dir,
- primary: false,
- rustc_version: rustc_version.as_slice(),
- resolve: resolve,
- package_set: deps,
- config: config,
- dylib: (parts.get(0).to_string(), parts.get(1).to_string())
- };
+ try!(cx.prepare(pkg));
// Build up a list of pending jobs, each of which represent compiling a
// particular package. No actual work is executed as part of this, that's
try!(compile(targets.as_slice(), dep, &mut cx, &mut jobs));
}
- cx.primary = true;
- cx.dest = &target_dir;
+ cx.primary();
try!(compile(targets, pkg, &mut cx, &mut jobs));
// Now that we've figured out everything that we're going to do, do it!
// TODO: Can a fingerprint be per-target instead of per-package? Doing so
// would likely involve altering the granularity of key for the
// dependency queue that is later used to run jobs.
- let fingerprint_loc = cx.dest.join(format!(".{}.fingerprint",
- pkg.get_name()));
+ let fingerprint_loc = cx.dest().join(format!(".{}.fingerprint",
+ pkg.get_name()));
let (is_fresh, fingerprint) = try!(is_fresh(pkg, &fingerprint_loc, cx,
targets));
util::to_hex(hasher.hash(&(package, profiles)))
}
-fn mk_target(target: &Path) -> Result<(), IoError> {
- io::fs::mkdir_recursive(target, io::UserRWX)
-}
-
fn compile_custom(pkg: &Package, cmd: &str,
cx: &Context) -> Job {
// FIXME: this needs to be smarter about splitting
let mut cmd = cmd.split(' ');
let mut p = util::process(cmd.next().unwrap())
.cwd(pkg.get_root())
- .env("OUT_DIR", Some(cx.dest.as_str().expect("non-UTF8 dest path")))
- .env("DEPS_DIR", Some(cx.dest.join(cx.deps_dir)
- .as_str().expect("non-UTF8 deps path")))
+ .env("OUT_DIR", Some(cx.dest().as_str()
+ .expect("non-UTF8 dest path")))
+ .env("DEPS_DIR", Some(cx.deps_dir.as_str()
+ .expect("non-UTF8 deps path")))
.env("TARGET", cx.config.target());
for arg in cmd {
p = p.arg(arg);
let root = package.get_root();
log!(5, "root={}; target={}; crate_types={}; dest={}; deps={}; verbose={}",
- root.display(), target, crate_types, cx.dest.display(),
+ root.display(), target, crate_types, cx.dest().display(),
cx.deps_dir.display(), cx.primary);
let primary = cx.primary;
into.push(crate_type.to_string());
}
- let out = cx.dest.clone();
+ let out = cx.dest().clone();
let profile = target.get_profile();
if profile.get_opt_level() != 0 {
fn build_deps_args(dst: &mut Args, package: &Package, cx: &Context) {
dst.push("-L".to_string());
- dst.push(cx.dest.display().to_string());
+ dst.push(cx.dest().display().to_string());
dst.push("-L".to_string());
dst.push(cx.deps_dir.display().to_string());
- for target in dep_targets(package, cx).iter() {
+ for target in cx.dep_targets(package).iter() {
dst.push("--extern".to_string());
dst.push(format!("{}={}/{}",
target.get_name(),
cx.deps_dir.display(),
- target_filename(target, cx)));
- }
-}
-
-fn target_filename(target: &Target, cx: &Context) -> String {
- let stem = target.file_stem();
-
- if target.is_dylib() {
- let (ref prefix, ref suffix) = cx.dylib;
- format!("{}{}{}", prefix, stem, suffix)
- } else if target.is_rlib() {
- format!("lib{}.rlib", stem)
- } else {
- unreachable!()
- }
-}
-
-fn dep_targets(pkg: &Package, cx: &Context) -> Vec<Target> {
- match cx.resolve.deps(pkg.get_package_id()) {
- None => vec!(),
- Some(deps) => deps
- .map(|pkg_id| {
- cx.package_set.iter()
- .find(|pkg| pkg_id == pkg.get_package_id())
- .expect("Should have found package")
- })
- .filter_map(|pkg| {
- pkg.get_targets().iter().find(|&t| t.is_lib() && t.get_profile().is_compile())
- })
- .map(|t| t.clone())
- .collect()
+ cx.target_filename(target)));
}
}